/*******************************************************************************
 * This file is part of Crunch Network.
 *
 * Crunch Network is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Network is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Network.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.network;

import com.crunch.network.util.BitStreamReader;
import com.crunch.network.util.BitStreamWriter;

import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;

/**
 * Instances of {@code Message} subclasses store messages sent over the network. Each message subclass is identified
 * with a unique integer ID which should match the ID passed into the {@link MessageRegistry#register(int,
 * MessageRegistry.MessageFactory)} method.
 *
 * The {@code Message} class has three important methods which must be overridden in subclasses: {@link Message#read
 * (crunch.network.util.BitStreamReader)}, {@link Message#write(com.crunch.network.util.BitStreamWriter)},
 * and {@link Message#execute()}.
 *
 * {@link Message#read(com.crunch.network.util.BitStreamReader)} and
 * {@link Message#write(com.crunch.network.util.BitStreamWriter)} are used to read and write data to and from network
 * packets, respectively. These methods should be "compatible" with each other in that data which is successfully
 * written with the {@code write()} method can be successfully read with the {@code read()} method. The data written by
 * these methods should not depend on any external state.
 *
 * If {@code read()} attempts to read past the end of the packet, a {@link java.nio.BufferUnderflowException} should be
 * thrown. Despite being an unchecked exception, it will be caught and handled in this case, as this is a network data
 * error which could be due to a number of reasons and not necessarily a programming error.
 *
 * If {@code write()} attempts to write past the end of the packet, a {@link java.nio.BufferOverflowException} should
 * be thrown. In fact, this is <i>not</i> an error at all and simply indicates that the packet ran out of space.
 * Guaranteed messages are automatically queued and sent in the next packet if this occurs.
 *
 * Once data has been successfully read, the {@link Message#execute()} method will (usually) be called ({@code
 * execute()} will be skipped if it is detected that this message is a duplicate). There may be a delay between when
 * {@code read()} and {@code execute()} are called. The {@code execute()} method is where the data inside the method
 * should be used.
 */
public abstract class Message {
	/**
	 * The constructor takes the message type ID.
	 *
	 * @param   id the message type ID. This ID should match the ID passed into the {@link MessageRegistry#register
	 *          (int, crunch.network.MessageRegistry.MessageFactory)} method.
	 */
	protected Message(int id) {
		this.id = id;
	}

	/**
	 * Reads message data from the stream provided. The data read should be "compatible" with the data written in the
	 * corresponding {@link Message#write(com.crunch.network.util.BitStreamWriter)} method.
	 *
	 * @param   reader the stream from which message data should be read.
	 * @throws  BufferUnderflowException if too little data is available for reading.
	 * @throws  IOException if an error in the data is detected.
	 */
	public abstract void read(BitStreamReader reader) throws BufferUnderflowException, IOException;

	/**
	 * Writes message data to the stream provided. The data written should be "compatible" with the data read in the
	 * corresponding {@link Message#read(com.crunch.network.util.BitStreamReader)} method.
	 *
	 * @param   writer the stream to which message data should be written.
	 * @throws  BufferOverflowException if too much data is available for writing.
	 */
	public abstract void write(BitStreamWriter writer) throws BufferOverflowException;

	/**
	 * Called to perform an action based on the data stored in the message.
	 */
	public abstract void execute();

	/**
	 * Returns the message type ID.
	 *
	 * @return  the message type ID.
	 */
	public int getId() {
		return id;
	}

	// message type ID
	private int id;
}
